#ifndef RESOURCE_H
#define RESOURCE_H

#include <jimix/Globals.h>

/**
  This is an empty class that other classes must derive from
  in order to have their functions called by a resource.
  
  NOTE: Any class which extends this MUST extend it as it's FIRST subclass, i.e.
    class A: public Callable, public B {
  NOT
    class A: public B, public Callable {
  This is for downcasting reasons.
**/
class Callable
{
public:
  Callable() {}
  ~Callable() {}
};

class Resource
{
public:
  typedef int (Callable::*Call0)();
  typedef int (Callable::*Call1)(int);
  typedef int (Callable::*Call2)(int,int);
  typedef int (Callable::*Call3)(int,int,int);
  typedef int (Callable::*Call4)(int,int,int,int);
  typedef int (Callable::*Call5)(int,int,int,int,int);
};

/**
  This is the common syscall code which passes control to the kernel.
**/
int callResourceUser(u32int rId, u32int fId, int p1=0, int p2=0, int p3=0, int p4=0);

/**
  Calls the function with the given function ID on the resource with ID rId. This is called from user mode.
**/
template <class P1,class P2,class P3,class P4>
int callResource(u32int rId, const int fId, P1 p1=(P1)0, P2 p2=(P2)0, P3 p3=(P3)0, P4 p4=(P4)0)
{
  return callResourceUser(rId, (u32int)fId, (int)p1, (int)p2, (int)p3, (int)p4);
}

template <class P1,class P2,class P3>
int callResource(u32int rId, const int fId, P1 p1=(P1)0, P2 p2=(P2)0, P3 p3=(P3)0)
{
  return callResourceUser(rId, (u32int)fId, (int)p1, (int)p2, (int)p3);
}

template <class P1,class P2>
int callResource(u32int rId, const int fId, P1 p1=(P1)0, P2 p2=(P2)0)
{
  return callResourceUser(rId, (u32int)fId, (int)p1, (int)p2);
}

template <class P1>
int callResource(u32int rId, const int fId, P1 p1=(P1)0)
{
  return callResourceUser(rId, (u32int)fId, (int)p1);
}

template <class P1>
int callResource(u32int rId, const int fId)
{
  return callResourceUser(rId, (u32int)fId);
}

int callResource(u32int rId, const int fId);

/**
  Requests a resource. Returns -1 on failure.
**/
u32int requestResource(const char *name);

/**
  Creates a new resource (RemoteResource).
**/
int createResource(Callable *target, const char *name=NULL);

/**
  Adds a new function to the given resource.
**/
int addFunction(int resource, Resource::Call0 pCall, int fId);
int addFunction(int resource, Resource::Call1 pCall, int fId);
int addFunction(int resource, Resource::Call2 pCall, int fId);
int addFunction(int resource, Resource::Call3 pCall, int fId);
int addFunction(int resource, Resource::Call4 pCall, int fId);
int addFunction(int resource, Resource::Call5 pCall, int fId);

/**
  Destroys the resource.
**/
int destroyResource(int resource);

inline void outb(u16int port, u8int value)
{
  asm volatile ("outb %1, %0" : : "dN" (port), "a" (value));
}

inline u8int inb(u16int port)
{
  u8int ret;
  asm volatile ("inb %1, %0" : "=a" (ret) : "dN" (port));
  return ret;
}

inline u16int inw(u16int port)
{
  u16int ret;
  asm volatile ("inw %1, %0" : "=a" (ret) : "dN" (port));
  return ret;
}

#endif
